package org.openqa.selenium.profiler;

import static org.junit.Assert.*;
import static org.openqa.selenium.profiler.testserver.AppServer.*;

import java.io.File;
import java.io.IOException;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.After;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.profiler.testserver.DelayedHttpHandler;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.webbitserver.handler.StringHttpHandler;

import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.base.Splitter;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.io.Files;

public class LoggingTest {

  WebDriver driver;

  File logFile;

  public void startDriver() {
    DesiredCapabilities capabilities = DesiredCapabilities.firefox();
    logFile = enableProfiler(capabilities);
    driver = new FirefoxDriver(capabilities);
  }

  @After
  public void killDriver() {
    if (driver != null) {
      driver.quit();
      driver = null;
    }
  }

  @After
  public void deleteLogFile() {
    if (logFile.exists()) {
      logFile.delete();
    }
  }

  private static File enableProfiler(DesiredCapabilities capabilities) {
    File logFile;
    try {
      logFile = File.createTempFile("seprofilelogfile", ".log");
    } catch (IOException e) {
      throw Throwables.propagate(e);
    }
    capabilities.setCapability("seprofiler.logfile", logFile.getAbsolutePath());
    return logFile;
  }

  private List<String> getLogEntries() {
    String file;
    try {
      file = Files.toString(logFile, Charsets.UTF_8);
      String json = "[" + Joiner.on(',').join(Splitter.on('\n').split(file)) + "]";
      JSONArray array = new JSONArray(json);
      ImmutableList.Builder<String> builder = new ImmutableList.Builder<String>();
      for (int i = 0; i < array.length(); ++i) {
        builder.add(array.get(i).toString());
      }
      return builder.build();
    } catch (IOException e) {
      throw Throwables.propagate(e);
    } catch (JSONException e) {
      throw Throwables.propagate(e);
    }
  }

  @Test
  public void testLogsHttpStartAndEnd() throws Exception {
    startDriver();

    List<String> httpCommandEntries = ImmutableList.copyOf(Iterables.filter(getLogEntries(), new Predicate<String>() {
      @Override
      public boolean apply(String logEntry) {
        return logEntry.contains(ProfilerLogger.Event.HTTP_COMMAND.toString());
      }
    }));

    assertEquals(2, httpCommandEntries.size());
  }

  @Test
  public void testLogsMultipleCommands() throws Exception {
    startDriver();
    driver.get(serveBodyOnce("<div name='q'></div>"));
    driver.findElement(By.name("q"));

    List<String> httpCommandEntries = getHttpCommandEntries();

    assertEquals(6, httpCommandEntries.size());
  }

  @Test
  public void testLogsPageLoadingTime() throws Exception {
    startDriver();
    driver.get(serveHandlerOnce(new DelayedHttpHandler(2500, new StringHttpHandler("text/html", "foo"))));
    driver.findElement(By.tagName("body")).getText();
    ImmutableList<String> yieldLogEntries = filterLogEntriesByString(ProfilerLogger.Event.YIELD_TO_PAGE_LOAD);
    long time = new JSONObject(yieldLogEntries.get(yieldLogEntries.size() - 1)).getLong("end") -
        new JSONObject(yieldLogEntries.get(0)).getLong("start");
    assertTrue(time > 2500);
  }

  private ImmutableList<String> getHttpCommandEntries() {
    return filterLogEntriesByString(ProfilerLogger.Event.HTTP_COMMAND);
  }

  private ImmutableList<String> filterLogEntriesByString(final Object string) {
    return ImmutableList.copyOf(Iterables.filter(getLogEntries(), new Predicate<String>() {
      @Override
      public boolean apply(String logEntry) {
        return logEntry.contains(string.toString());
      }
    }));
  }
}
